<?php
use Swoole\Coroutine\Channel;
use Swoole\Coroutine\MySQL;

class MysqlPool {
    
	private   $min;
    private   $max;
    private   $count;
    private   $connections;
    protected $freeTime;
	private   $config;

    public static $instance;

    public function __construct()
    {
        $this->min = 10;
        $this->max = 100;
        $this->freeTime = 10 * 3600;
        $this->connections = new Channel($this->max + 1);
    }

    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    protected function createConnection()
    {
        $conn = new MySQL();
        $conn->connect($this->config);

        return $conn;
    }

    protected function createConnObject()
    {
        $conn = $this->createConnection();
        return $conn ? ['last_used_time' => time(), 'conn' => $conn] : null;
    }

    public function init($config)
    {
		$this->config = $config;
		
        for ($i = 0; $i < $this->min; $i++) {
            $obj = $this->createConnObject();
            $this->count++;
            $this->connections->push($obj);
        }

        return $this;
    }

    public function getConn($timeout = 3)
    {
        if ($this->connections->isEmpty()) {
            if ($this->count < $this->max) {
                $this->count++;
                $obj = $this->createConnObject();
            } else {
                $obj = $this->connections->pop($timeout);
            }
        } else {
            $obj = $this->connections->pop($timeout);
        }

        return $obj['conn']->connected ? $obj['conn'] : $this->getConn();
    }

    public function recycle($conn)
    {
        if ($conn->connected) {
            $this->connections->push(['last_used_time' => time(), 'conn' => $conn]);
        }
    }

    public function recycleFreeConnection()
    {
        swoole_timer_tick(2 * 60 * 1000, function () {
			
			if ($this->connections->length() < intval($this->max * 0.5)) {
				return;
			}

			while (true) {
				
				if ($this->connections->isEmpty()) {
					break;
				}

				$connObj = $this->connections->pop(0.001);
				$nowTime = time();
				$lastUsedTime = $connObj['last_used_time'];
			   
				if ($this->count > $this->min && ($nowTime - $lastUsedTime > $this->freeTime)) {
					$connObj['conn']->close();
					$this->count--;
				} else {
					$this->connections->push($connObj);
				}
			}
        });
    }
}
